#include <stdio.h>  /* -*- c -*- */

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

#include <libspe2.h>

#define SendInt(CTX, VAL) spe_in_mbox_write(CTX, \
			(unsigned int *) &VAL, \
			1, SPE_MBOX_ANY_NONBLOCKING);
#define GetInt(CTX, VAL)  spe_out_intr_mbox_read(CTX,\
			(unsigned int *) &VAL, \
			1, SPE_MBOX_ALL_BLOCKING);

int getResultat(spe_context_ptr_t context, int val)
{
  int resultat;
  SendInt(context, val);
  GetInt(context, resultat);
  return resultat;
}  

spe_context_ptr_t ctx;
pthread_t pthread;
typedef int (*pifi)(int);
pifi convertionFunction;

#cpu cell

void *waitingThread(void * arg)
{
  spe_program_handle_t *spu_handle;
  spe_stop_info_t stop_info;
  insn *insnbuffer;
  spe_context_ptr_t ctx = *(spe_context_ptr_t *) arg;  
  unsigned int entry = SPE_DEFAULT_ENTRY;

  /* Load wrapper SPU code */
  spu_handle = spe_image_open("simple-worker-cell");
  assert(NULL != spu_handle);
  spe_program_load(ctx, spu_handle);
  /* Send and launch the code */
  spe_context_run(ctx, &entry, 0, convertionFunction, NULL, &stop_info);
  free(insnbuffer);
  pthread_exit(0);
}

pifi rpnCompile(char *expr)
{
  insn *code; 
  int top = 3;

  posix_memalign(&code, 16, 1024);
  #[ .org code ]#


  while (*expr)
    {
      char buf[32];
      int n ;

      if (sscanf(expr, "%[0-9]%n", buf, &n))
	{
	  expr += n - 1;
	  n = strtol (buf, 0, 0);
	  if (top == 30)
	    {
	      fprintf(stderr, "expression too complex");
	      exit(0);
	    }
	  ++top;
	  #[		il	$(top), (n)  ]#;
	}
      else if (*expr == '+')
	{ --top; #[ a  $(top), $(top), $(top+1)	 ]# }
      else if (*expr == '-')
	{ --top; #[ sf  $(top), $(top), $(top+1) ]# }
      else if (*expr == '*')
	{ --top; #[ mpy $(top), $(top), $(top+1) ]# }
      else if (*expr == '/') 
	{ int op0, op1, op2, op3  = top + 1;
	  int op4  = top + 2;
	  --top; 
	  op0 = op1 = top;
	  op2 = top+1;
	  /* gcc-4.3.2/gcc/config/spu/spu.md (div<mode>3_fast */
	  /* op0 = top, op1 = top, op2=top+1, op3, op4 */
	  #[ 
		   csflt $(op1), $(op1), 0
		   csflt $(op2), $(op2), 0

		   frest $(op3),  $(op2) 
		   fi    $(op3),  $(op2), $(op3)
		   fm    $(op4),  $(op1), $(op3)
		   fnms  $(top),  $(op4), $(op2), $(top)
		   fma   $(top),  $(top), $(op3), $(op4)

		   cflts $(top),  $(top), 0
	  ]# 
	}
      else
	{
	  fprintf(stderr, "cannot compile: %s\n", expr);
	  abort();
	}
      ++expr;
    }
  if (3 != top)
    {
      fprintf(stderr, "stack error : %d\n", top);
      abort();
    }
  #[  bi $lr  ]#;
  iflush(code, hpbcg_asm_pc);
  if ((int)(hpbcg_asm_pc - code) >= 64)
    {
      fprintf(stderr, "expression too complex");
      exit(0);
    }
  ctx = spe_context_create(0, NULL);	 /* Send it to worker */
  if (NULL == ctx) perror("create_context\n");
  pthread_create(&pthread, NULL, &waitingThread, &ctx);
  return (pifi)code;
}

int main(int argc, char * argv[])
{
  int i;

  convertionFunction = rpnCompile("9*5/32+");
  printf("\nC:");
  for (i = 0; i <= 100; i+= 10) printf("%3d ", i);
  printf("\nF:");
  i = 11; 		/* Send the limit iterator */
  SendInt(ctx, i);
  for (i = 0; i <= 100; i+= 10) printf("%3d ", getResultat(ctx, i));
  printf("\n");
  assert(0 == spe_context_destroy(ctx));

  // convertionFunction = rpnCompile("32-5*9/");
  convertionFunction = rpnCompile("2/");
  printf("\nF:");
  for (i = 32; i <= 212; i+= 10) printf("%3d ", i);
  printf("\nC:");
  i = 19;
  SendInt(ctx, i);
  for (i = 32; i <= 212; i+= 10) printf("%3d ", getResultat(ctx, i));
  printf("\n");
  assert(0 == spe_context_destroy(ctx));
  return 0;
}
